-- UIHeroAttrib
-- Created by chengb Mar/26/2015
-- 英雄属性界面

require "game/ui/form/hero/UISkillUpgrade"
require "game/ui/form/hero/UISkillUpgrade2"
require "game/ui/form/hero/UISkillLearn"
require "game/ui/form/skill/UISkillOperation"
require "game/ui/form/hero/UICareerLine"

UIHeroAttrib = class("UIHeroAttrib", function()
    return cc.Layer:create();
end)

function UIHeroAttrib.create()
    return UIHeroAttrib.new();
end

-- 技能系别按钮的数量
local SKILL_STYLE_NUM = 4;

-- 模型缩放比例
local HERO_MODEL_SCALE = 0.6667

-- 技能框的个数
local MAX_SKILL_NUM = 5;

-- 职业最高阶位
local MAX_RANK = 5;

-- 最多可显示的星星数量
local MAX_STAR_NUM = 5;

-- 特性最大数量
local MAX_FEATURE_NUM = 3;

-- 技能所处状态
local SKILL_STATE_UNKOWN        = 0;    -- 未知
local SKILL_STATE_LEARNED       = 1;    -- 可升级
local SKILL_STATE_CAN_LEARN     = 2;    -- 可学习

function UIHeroAttrib:ctor()
    -- 初始化
    self:setName("UIHeroAttrib");
    local node = cc.CSLoader:createNode("layout/hero/HeroAttrib.csb");
    self:addChild(node);
    self.node = node;

    self:setLocalZOrder(UIMgr.TOP_MENU_ZORDER + 1);

    local pet = ME.user:getActivePet();
    if not pet then
        return;
    end

    -- 设置pet的背景可见
    self:showHeroBg();

    -- 记录当前出战英雄
    local classId = pet.classId;
    self.pet = pet;
    self.classId = classId;

    -- 默认选择第一个分页
    self.curPage = 1;

    -- 注册事件处理回调函数
    self:registerEventCallback();

    -- 注册点击事件
    self:registerTouchEvent();

    -- 初始化窗口
    self:initForm();

    -- 重绘
    self:redraw();

    -- 适配
    self:resize();
end

-- 适配
function UIHeroAttrib:resize()
    local node = self.node;

    -- bg居中显示并充满整个屏幕
    AlignM.fitToScreen(node, "bg");

    -- CT居中并缩放至合适大小
    AlignM.alignToCenter(node, "CT");

    -- BT置于底部并缩放至合适大小
    AlignM.alignToBottomCenter(node, "BT");
end

-- 注册事件处理回调函数
function UIHeroAttrib:registerEventCallback()
    EventMgr.removeAll("UIHeroAttrib");

    -- 注册学习职业成功的事件回调
    EventMgr.register("UIHeroAttrib", event.JOIN_CAREER_ANIM_DONE, function()
        -- 重绘界面
        self:redraw();
        UIDungeonMgr.getCurLevel():syncProps();
        UIDungeonMgr.getCurLevel():updateUI();
    end);

    -- 状态刷新
    EventMgr.register("UIHeroAttrib", event.USER_INFO_UPDATED, function(args)
        if args == "clear_prop" then
            self:redrawStatus();
        end
    end);

    -- 注册获得焦点的回调
    EventMgr.register("UIHeroAttrib", event.FOCUS_CHANGED, function(para)
        if para["get"] == "UIHeroAttrib" then
            UIMgr:getCurrentScene().topMenu:show(TOPMENU_FRAME_SP, TOPMENU_FRAME_GEM);
            UIMgr:getCurrentScene().topMenu:setLocalZOrder(UIMgr.TOP_MENU_ZORDER + 1);
        end
    end);

    -- 注册引导到能够学习称号后的回调
    EventMgr.register("UIHeroAttrib", event.CAN_LEARN_TITLE, function()
        -- 称号可学习的界面动画
        -- 新手引导，故肯定是第一个item
        local skillPanel = findChildByName(self.node, "CT/skill_panel");
        local child = findChildByName(skillPanel, "skill" .. 1);
        self:learnTitleAnimation(child);
    end);


    EventMgr.register("UIHeroAttrib", event.UPGRADE_CAREER, function(args)
        local careerId = args.careerId;
        local level    = args.level;

        local rank = CareerM.getCareerInfo(careerId, "rank");
        if rank >= MAX_RANK then
            return;
        end

        -- 播完动画回调
        local function callback()
            -- 提示
            local name = CareerM.getCareerInfo(careerId, "name");
            local msg = string.format(getLocStr("upgrade_career_succ"), name, level);
            local ret = args.ret;

            if type(ret) == "string" then
                alert(msg .. "\n" .. ret);
            else
                alert(msg);
            end

            -- 重绘界面
            local function showBonus()
                self:redrawSkill();

                local function redraw()
                    local uiForm = UIMgr.getCurrentScene():getFormByName("UIHeroAttrib");

                    if uiFrom then
                        uiFrom:redraw();
                    end

                    -- 重绘界面
                    UIDungeonMgr.getCurLevel():syncProps();
                    UIDungeonMgr.getCurLevel():updateUI();
                end

                if ret ~= nil and #ret > 0 then
                    showBonusNotifyForm(getLocStr("upgrade_career_bonus"), ret, nil, nil, nil, nil, nil, redraw);
                else
                    redraw();
                end
            end
            performWithDelay(self, showBonus, 0.5);
        end

        -- 播放动画
        local child = self:getSkillChild(careerId);
        local icon = child:getChildByName("skill_icon");
        local layer = findChildByName(child, "star_node/layer");

        local star = findChildByName(child, "star_node/layer/star"..level);
        star:setVisible(false);
        local starEffectNode = cc.Node:create();
        starEffectNode:setPosition(cc.p(star:getPositionX(), 0));
        layer:addChild(starEffectNode);

        playUpgradeSkillEffect(icon, starEffectNode, callback);
    end);

    -- 界面析构后，需要清理下
    self:registerScriptHandler(function(ev)
        if ev == "exit" then
            cclog("UIHeroAttrib 界面析构清理");

            EventMgr.removeAll("UIHeroAttrib");

            -- 还原TopMenu的ZOrder
            local topMenu = UIMgr:getCurrentScene().topMenu;
            if topMenu then
                topMenu:setVisible(self.oldVisible);
                topMenu:setLocalZOrder(self.oldZOrder);
            end

            if UIDungeonMgr:getCurLevel() then
                UIDungeonMgr:getCurLevel():showMenu();
            end
        elseif ev == "enter" then
            if UIDungeonMgr:getCurLevel() then
                UIDungeonMgr:getCurLevel():hideMenu();
            end

            -- 临时调高TOP_MENU的ZOrder
            local topMenu = UIMgr:getCurrentScene().topMenu;
            self.oldVisible = topMenu:isVisible();
            UIMgr.getCurrentScene().topMenu:setVisible(true);
            self.oldZOrder = topMenu:getLocalZOrder();

            -- 标签落下的效果
            self:playTabEffect();
        end
    end);
end

-- 标签落下的效果
function UIHeroAttrib:playTabEffect()
    local vOffset = 150;

    local function play()
        -- 掉落并小幅回弹
        local function dropDown(node, index)
            local pos = cc.p(node:getPosition());
            local delay = cc.DelayTime:create(0.2 * (index - 1));
            local moveDown = cc.MoveTo:create(0.2, cc.p(pos.x, pos.y - vOffset - 5));
            local moveBack = cc.MoveTo:create(0.2, cc.p(pos.x, pos.y - vOffset));
            local action = cc.Sequence:create(delay, moveDown, moveBack);
            node:runAction(action);
        end

        for i = 1, SKILL_STYLE_NUM do
            local btn = self.btns[i];
            local light = self.lights[i];

            dropDown(btn, i);
            dropDown(light, i);
        end
    end

    -- tab的动画效果
    performWithDelay(self, play, 0.2);
end

-- 注册点击事件
function UIHeroAttrib:registerTouchEvent()
    -- 注册返回按钮点击事件
    local btnBack = findChildByName(self.node, "BT/btn_back");
    local function onBackClick(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            -- 关闭当前界面
            AudioM.playFx("button_return");
            UIMgr.getCurrentScene():removeFormByName("UIHeroAttrib");
        end
    end
    AddTouchEventListener(btnBack, onBackClick);
    AddBackEventHandler(self, btnBack, btnBack.touchEventListener);
    TextStyleM.setTextStyle(btnBack, TextStyleM.TEXT_SIZE_MIDDLE, TextStyleM.TEXT_COLOR_BROWN_YELLOW, true, nil, nil, nil, nil, nil, true);
    btnBack:setTitleText(getLocStr("btn_text_back"));

    -- 注册专属技能图标点击事件
    local skillImg = findChildByName(self.node, "CT/left_part/skill_node/special_name");
    skillImg:setTouchEnabled(true);
    local function onSkillClick(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            AudioM.playFx("button_caption");
            -- 显示技能操作页面
            if UIMgr.getCurrentScene():isOpen("UISkillOperation") then
                UIMgr.getCurrentScene():removeFormByName("UISkillOperation");
            end

            local uiSkillOperation = UISkillOperation.create(sender.skillId);
            UIMgr.getCurrentScene():addForm(uiSkillOperation);
        end
    end
    skillImg:addTouchEventListener(onSkillClick);

    -- 注册主从专属技能事件

    local specialSkill = self.pet:getSpecial();
    specialSkill = FormulaM.invoke("CALC_FINAL_SKILL", self.pet, specialSkill);
    local lictors = ME.user.dbase:query("lictor_pets") or {};

    local leftPart  = findChildByName(self.node, "CT/left_part");
    local heroPanel = findChildByName(leftPart, "hero_panel")
    heroPanel:setTouchEnabled(true);
    local function onPetSkillClick(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            AudioM.playFx("button_caption");
            -- 显示技能操作页面
            if UIMgr.getCurrentScene():isOpen("UISkillOperation") then
                UIMgr.getCurrentScene():removeFormByName("UISkillOperation");
            end

            local uiSkillOperation = UISkillOperation.create(specialSkill);
            UIMgr.getCurrentScene():addForm(uiSkillOperation);
         end
    end
    heroPanel:addTouchEventListener(onPetSkillClick);

    -- 注册扈从专属技能点击回调
    local function onLictorClick(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            AudioM.playFx("button_caption");
            -- 显示技能操作页面
            if UIMgr.getCurrentScene():isOpen("UISkillOperation") then
                UIMgr.getCurrentScene():removeFormByName("UISkillOperation");
            end

            local pet = PetM.getMyPet(sender.petId);
            if pet == nil then
                return;
            end

            local lictorSpecialId = pet:getSpecial();
            lictorSpecialId = FormulaM.invoke("CALC_FINAL_SKILL",pet, lictorSpecialId);
            local uiSkillOperation = UISkillOperation.create(lictorSpecialId, true);
            UIMgr.getCurrentScene():addForm(uiSkillOperation);
        end
    end
    local num = 0;
    for _, petId in ipairs(lictors) do
        -- if LictorM.canDispatch(petId) then
            num = num + 1;

            local lictorPanel = findChildByName(leftPart, "lictor_panel_" .. tostring(num));

            if lictorPanel then
                lictorPanel:setTouchEnabled(true);
                lictorPanel.petId = petId;

                lictorPanel:addTouchEventListener(onLictorClick);
            end
        -- end
    end

    local careerLineImg = findChildByName(self.node, "CT/career_line");
    local careerBlue = findChildByName(self.node, "CT/career_blue");
    local function onCareerLineClick(sender, eventType)
        if eventType == ccui.TouchEventType.began then
            -- 3.变小
            local downTime  = 0.25/2;
            local down2Time = 0.25/2;
            local scaleDown = cc.ScaleTo:create(downTime, 0.8);
            local scaleDown2 = cc.ScaleTo:create(downTime, 0.8);
            careerBlue:runAction(scaleDown);
            careerLineImg:runAction(scaleDown2);

        end
        if eventType == ccui.TouchEventType.ended then
            -- 3.变小
            local downTime  = 0.25/2;
            local down2Time = 0.25/2;
            local scaleUp = cc.ScaleTo:create(downTime, 1);
            local scaleUp2 = cc.ScaleTo:create(down2Time, 1);
            careerBlue:runAction(scaleUp);
            careerLineImg:runAction(scaleUp2);

            -- 显示技能操作页面
            if UIMgr.getCurrentScene():isOpen("UICareerLine") then
                UIMgr.getCurrentScene():removeFormByName("UICareerLine");
            end

            local uiCareerLine = UICareerLine.create(self.curPage);
            UIMgr.getCurrentScene():addForm(uiCareerLine);
            AudioM.playFx("ui_open1");
        end
        if eventType == ccui.TouchEventType.canceled then
            -- 3.变小
            local downTime  = 0.25/2;
            local down2Time = 0.25/2;
            local scaleUp = cc.ScaleTo:create(downTime, 1);
            local scaleUp2 = cc.ScaleTo:create(down2Time, 1);
            careerBlue:runAction(scaleUp);
            careerLineImg:runAction(scaleUp2);
        end
    end
    careerLineImg:addTouchEventListener(onCareerLineClick);
    careerBlue:addTouchEventListener(onCareerLineClick);
end

-- 整理状态效果
function UIHeroAttrib:getStatusEffect(sender)
    if sender.propId ~= 507 then
        -- 不是诅咒状态，返回原值
        return sender.effect;
    else
        -- 根据玩家的诅咒效果削减计算显示用的效果
        local p = PropM.combine(ME.user, "curse_resist", 1);
        local effect = PropM.combine(ME.user, "curse", 1);

        -- 最多削减到0
        p = math.max(1000 - p[3], 0);
        return string.gsub(sender.effect, tostring(effect[3] / 10), tostring(math.modf(effect[3] * p / 1000 / 10)));
    end
end

-- 创建英雄模型
function UIHeroAttrib:createHeroModel()
    local heroPanel = findChildByName(self.node, "CT/left_part/hero_panel");
    local headImg   = findChildByName(heroPanel, "hero_head");
    local modelNode = findChildByName(heroPanel, "model_node");

    -- 获取当前出战英雄
    local iconPath = getHeroLargeIconPath(self.pet:getIcon());
    modelNode:removeAllChildren();
    createHeroModel(self.pet:getModelId(), headImg, modelNode, HERO_MODEL_SCALE, iconPath);
end

-- 创建变身后的英雄模型
function UIHeroAttrib:createShapeChangeModel(shapeId)
    local heroPanel = findChildByName(self.node, "CT/left_part/hero_panel");
    local headImg   = findChildByName(heroPanel, "hero_head");
    local modelNode = findChildByName(heroPanel, "model_node");

    -- 查询变身信息
    local shapeInfo = ShapeChangeM.query(shapeId);

    -- 获取当前出战英雄
    local iconPath = getHeroLargeIconPath(shapeInfo["icon"]);
    local animation = shapeInfo["animation"];
    modelNode:removeAllChildren();
    createShapeChangeMode(shapeId, headImg, animation, modelNode, HERO_MODEL_SCALE, iconPath);
end

-- 重绘
function UIHeroAttrib:redraw()
    -- 设置英雄名字
    local petInfo = PetM.query(self.classId);
    local name = PetM.query(self.classId, "name");
    local titleLabel = findChildByName(self.node, "CT/bg_main/title_label");
    local offset = cc.p(0, 0);
    TextStyleM.setArtWordTitleStyle(titleLabel);
    titleLabel:setString(name);

    -- 设置英雄阶位
    local starNode = findChildByName(self.node, "CT/bg_main/star_node");
    local starPath = "images/ui/symbol/star_large.png";

    -- 如果冈布奥已觉醒，使用橙色的星星
    if AwakeM.isCarved(self.classId) then
        starPath = "images/ui/symbol/star_large3.png";
    end

    local starLayer = createStarLayer(self.pet.rank, starPath);
    if starLayer then
        starNode:removeAllChildren();
        starNode:addChild(starLayer);
    end

    -- 当前页的系别
    self:updateDept();

    -- 显示专属技能
    self:redrawSpecialSkill();

    -- 显示状态
    self:redrawStatus()

    -- 显示属性
    self:redrawAttrib();

    -- 显示技能
    self:redrawSkill();
end

-- 更新系别
function UIHeroAttrib:updateDept()
    local careerImg = findChildByName(self.node, "CT/career_img");
    local careerLabel = findChildByName(self.node, "CT/career_label");
    local departmentId = self:getCurDeptId();
    careerImg:loadTexture(getCareerDepartmentIconPath("department_".. departmentId));
    local depName = CareerM.getCareerTitle(departmentId);

    local kerning = -1;
    if getLang() == "th" then
        kerning = 1;
    end

    TextStyleM.setTextStyle(careerLabel, TextStyleM.TEXT_SIZE_MINI, TextStyleM.TEXT_COLOR_BEIGE, false, kerning, 1, true);
    careerLabel:setString(depName);
end

-- 显示专属技能
function UIHeroAttrib:redrawSpecialSkill()
     -- 显示专属技能图片
    local skillNode = findChildByName(self.node, "CT/left_part/skill_node");
    local specialSkill = self.pet:getSpecial();
    specialSkill = FormulaM.invoke("CALC_FINAL_SKILL", self.pet, specialSkill);
    local skillImg = findChildByName(skillNode, "special_name");
    local name = SkillM.query(specialSkill, "name");
    skillImg:setString(name);
    TextStyleM.setArtWordTitleStyle(skillImg);
    skillImg.skillId = specialSkill;

    -- 显示专属技能等级
    local level = SkillM.query(specialSkill, "level");

    for i = 1, MAX_SKILL_LEVEL do
        local pointImg = findChildByName(skillNode, "point" .. i);
        if i <= level then
            pointImg:loadTexture("images/ui/symbol/sign_pot1.png");
        else
            pointImg:loadTexture("images/ui/symbol/sign_pot2.png");
        end
    end
end

-- 重绘随扈的头像
function UIHeroAttrib:redrawLictors()
    -- 显示出战冈布奥的技能
    local skillNode = findChildByName(self.node, "CT/left_part/skills_node");
    local specialSkill = self.pet:getSpecial();
    specialSkill = FormulaM.invoke("CALC_FINAL_SKILL", self.pet, specialSkill);
    local icon         = SkillM.query(specialSkill, "icon");
    local heroSkill = findChildByName(skillNode, "pet_skill");
    local iconPath   = getSkillIconPath(icon);
    heroSkill:loadTexture(iconPath);

    -- 显示随扈的专属技能
    local lictors = ME.user.dbase:query("lictor_pets") or {};
    local lictorNum = 0;

    -- 遍历获取有效的扈从
    for _, petId in ipairs(lictors) do
        -- if LictorM.canDispatch(petId) then
        -- 老数据由于调整了阵营，通过能否链接来判断会有问题，所以这里给屏蔽了
        if petId > 0 then
            -- 有链接的冈布奥，则统计数量
            -- 未链接时记录值为 0
            lictorNum = lictorNum + 1;
        end
        -- end
    end

    if lictorNum == 1 then
        self:createLictorModel(lictors[1], 1, 0.25);

        -- 调整位置
        local lictorPanel = findChildByName(self.node, "CT/left_part/lictor_panel_1");
        lictorPanel:setPosition(lictorPanel:getPositionX() - 50, lictorPanel:getPositionY());

        local heroPanel = findChildByName(self.node, "CT/left_part/hero_panel");
        heroPanel:setPosition(heroPanel:getPositionX() - 50, heroPanel:getPositionY());
    elseif lictorNum == 2 then
        -- 创建随扈模型
        self:createLictorModel(lictors[1], 1, 0.5);
        self:createLictorModel(lictors[2], 2, 0.25);
    end
end

-- 显示状态
function UIHeroAttrib:redrawStatus()
   -- 里面改为不显示状态
end

-- 显示属性
function UIHeroAttrib:redrawAttrib()
    local attack = ME.user:getAttack();
    local magic = ME.user:getMagic();
    local hp = ME.user:getHp();
    local mp = ME.user:getMp();
    local maxHp = ME.user:getMaxHp();
    local maxMp = ME.user:getMaxMp();

    local rightPart = findChildByName(self.node, "CT/right_part");

    local modelNode = findChildByName(self.node, "CT/left_part/hero_panel/model_node");

    local kerning = -1;
    if getLang() == "th" then
        kerning = 1;
    end

    -- 攻击
    local nameLabel  = findChildByName(rightPart, "attack_node/name");
    local valueLabel = findChildByName(rightPart, "attack_node/value");
    TextStyleM.setTextStyle(nameLabel, TextStyleM.TEXT_SIZE_MINI, TextStyleM.TEXT_COLOR_BEIGE, false, kerning, 1, true);
    TextStyleM.setTextStyle(valueLabel, TextStyleM.TEXT_SIZE_MINI, TextStyleM.TEXT_COLOR_BEIGE, false, -1, 1, true);
    nameLabel:setString(getLocStr("attack"));
    valueLabel:setString(tostring(attack));

    -- 生命
    nameLabel  = findChildByName(rightPart, "hp_node/name");
    valueLabel = findChildByName(rightPart, "hp_node/value");
    TextStyleM.setTextStyle(nameLabel, TextStyleM.TEXT_SIZE_MINI, TextStyleM.TEXT_COLOR_BEIGE, false, kerning, 1, true);
    TextStyleM.setTextStyle(valueLabel, TextStyleM.TEXT_SIZE_MINI, TextStyleM.TEXT_COLOR_BEIGE, false, -1, 1, true);
    nameLabel:setString(getLocStr("hp"));
    valueLabel:setString(string.format(getLocStr("attrib_value_format"), hp, maxHp));

    -- 魔力
    nameLabel  = findChildByName(rightPart, "magic_node/name");
    valueLabel = findChildByName(rightPart, "magic_node/value");
    TextStyleM.setTextStyle(nameLabel, TextStyleM.TEXT_SIZE_MINI, TextStyleM.TEXT_COLOR_BEIGE, false, kerning, 1, true);
    TextStyleM.setTextStyle(valueLabel, TextStyleM.TEXT_SIZE_MINI, TextStyleM.TEXT_COLOR_BEIGE, false, -1, 1, true);
    nameLabel:setString(getLocStr("magic"));
    valueLabel:setString(tostring(magic));

    -- 魔法
    nameLabel  = findChildByName(rightPart, "mp_node/name");
    valueLabel = findChildByName(rightPart, "mp_node/value");
    TextStyleM.setTextStyle(nameLabel, TextStyleM.TEXT_SIZE_MINI, TextStyleM.TEXT_COLOR_BEIGE, false, kerning, 1, true);
    TextStyleM.setTextStyle(valueLabel, TextStyleM.TEXT_SIZE_MINI, TextStyleM.TEXT_COLOR_BEIGE, false, -1, 1, true);
    nameLabel:setString(getLocStr("mp"));
    valueLabel:setString(string.format(getLocStr("attrib_value_format"), mp, maxMp));

    -- 闪避
    nameLabel  = findChildByName(rightPart, "dodge_node/name");
    valueLabel = findChildByName(rightPart, "dodge_node/value");
    TextStyleM.setTextStyle(nameLabel, TextStyleM.TEXT_SIZE_MINI, TextStyleM.TEXT_COLOR_BEIGE, false, kerning, 1, true);
    TextStyleM.setTextStyle(valueLabel, TextStyleM.TEXT_SIZE_MINI, TextStyleM.TEXT_COLOR_BEIGE, false, -1, 1, true);
    nameLabel:setString(getLocStr("dodge"));
    valueLabel:setString(ME.user:getDodge() .. "%");

    -- 防御
    nameLabel  = findChildByName(rightPart, "defense_node/name");
    valueLabel = findChildByName(rightPart, "defense_node/value");

    TextStyleM.setTextStyle(nameLabel, TextStyleM.TEXT_SIZE_MINI, TextStyleM.TEXT_COLOR_BEIGE, false, kerning, 1, true);
    TextStyleM.setTextStyle(valueLabel, TextStyleM.TEXT_SIZE_MINI, TextStyleM.TEXT_COLOR_BEIGE, false, -1, 1, true);
    nameLabel:setString(getLocStr("defense"));
    local defense = FormulaM.invoke("CALC_USER_DEFENSE", ME.user);

    valueLabel:setString(defense);

    local addList = {};

    -- 攻击+N效果
    local delay = 0.01;
    if self.lastAttack ~= nil then
        local addValue =  attack - self.lastAttack;
        table.insert(addList, { "attack", addValue, cc.p(0, 50) });
    end
    self.lastAttack = attack;

    -- 魔力+N效果
    if self.lastMagic ~= nil then
        local addValue =  magic - self.lastMagic;
        table.insert(addList, { "magic", addValue, cc.p(0, 50) });
    end
    self.lastMagic = magic;

    -- 生命值+N效果
    if self.lastMaxHp ~= nil then
        local addValue =  maxHp - self.lastMaxHp;
        table.insert(addList, { "max_hp", addValue, cc.p(0, 50) });
    end
    self.lastMaxHp = maxHp;

    -- 魔法值+N效果
    if self.lastMaxmp ~= nil then
        local addValue =  maxMp - self.lastMaxmp;
        table.insert(addList, { "max_mp", addValue, cc.p(0, 50) });
    end
    self.lastMaxmp = maxMp;

    -- 播放特效
    local i = 1;
    for _, arr in pairs(addList) do
        local attrib = arr[1];
        local value  = arr[2];
        local offset = arr[3];
        if value > 0 then
            local function callFunc()
                playAttribUpEffect(modelNode, value, offset, attrib);
            end
            performWithDelay(self, callFunc, 0.01 + 0.8* (i - 1));
            i = i + 1;
        end
    end
end

-- 初始化窗口
function UIHeroAttrib:initForm()
    -- 处于变身状态，创建变身状态的模型，否则创建普通模型
    local shapeId = ShapeChangeM.getShapeChangeId();
    if type(shapeId) == "number" and shapeId ~= -1 then
        -- 获取当前的出战宠物
        local pet = ME.user:getActivePet();
        local userPetId = pet.classId;

        self:createShapeChangeModel(shapeId);
    else
        -- 创建英雄模型
        self:createHeroModel();
    end

    -- 显示随扈的模型
    self:redrawLictors();

    local skillPanel = findChildByName(self.node, "CT/skill_panel");
    local panelWidth  = skillPanel:getContentSize().width;
    local panelHeight = skillPanel:getContentSize().height;

    local posY = panelHeight/2;

    local iconWidth  = 90;
    local iconHeight = 90;

    -- 图标与图标直接的间距
    local gap = (panelWidth - iconWidth*MAX_SKILL_NUM) / (MAX_SKILL_NUM - 1);

    for i = 1, MAX_SKILL_NUM do
        local child = cc.CSLoader:createNode("layout/hero/SkillIcon.csb");
        child:setScale(0.9);
        child:setName("skill" .. i);
        local posX = iconWidth / 2 + (i - 1)*(iconWidth + gap + 1);
        child:setPosition(posX, posY);
        skillPanel:addChild(child);

        -- 注册技能框点击事件
        local skillFrame = findChildByName(child, "skill_frame");
        skillFrame:setTouchEnabled(true);
        local function onFrameClick(sender, eventType)
            if eventType == ccui.TouchEventType.ended then
                local careerId = child.careerId;
                -- 针对指引做特殊处理，如果没有到学习称号的指引步骤，则不允许学习称号
                if ME.user.forbidToLearnSkill == true then
                    alert(getLocStr("forbid_to_learn_skill"));
                    return;
                end

                -- 获取技能所处的状态
                local state = self:getCareerState(careerId);

                if state == SKILL_STATE_CAN_LEARN then
                    -- 打开技能学习界面
                    self:showSkillLearnForm();
                    AudioM.playFx("ui_open1");
                elseif state == SKILL_STATE_LEARNED then
                    -- 打开技能升级界面
                    self:showSkillUpgradeForm(careerId);
                    AudioM.playFx("ui_open1");
                else
                -- 其他情况，不做处理
                end
            end
        end
        AddTouchEventListener(skillFrame, onFrameClick);
    end

    -- 注册分页按钮点击事件
    local pagePart = findChildByName(self.node, "CT/page_part");
    self.pagePart = pagePart;
    for i = 1, SKILL_STYLE_NUM do
        local btn = findChildByName(pagePart, "btn_page" .. i);

        local function onBtnClick(sender, eventType)
            if eventType == ccui.TouchEventType.ended then
                AudioM.playFx("button_select");

                -- 针对指引做特殊处理，如果没有到学习称号的指引步骤，则不允许学习称号
                if ME.user.forbidToLearnSkill == true then
                    alert(getLocStr("forbid_to_learn_skill"));
                    return;
                end

                -- 设置选中效果
                self.curPage = i;
                self:setPageSelected();
            end
        end
        btn:addTouchEventListener(onBtnClick);
    end

    -- 初始化标签位置
    local vOffset = 150;
    local pagePart = findChildByName(self.node, "CT/page_part");
    self.btns = {};
    self.lights = {};
    for i = 1, SKILL_STYLE_NUM do
        local btn = findChildByName(pagePart, "btn_page" .. i);
        btn:setPositionY(btn:getPositionY() + vOffset);
        table.insert(self.btns, btn);
        local light = findChildByName(pagePart, "light" .. i);
        light:setPositionY(light:getPositionY() + vOffset);
        table.insert(self.lights, light);
    end
end

-- 获取当前页的系别
function UIHeroAttrib:getCurDeptId()
    local deptList = CareerM.getDepartmentList(self.pet);
    return deptList[self.curPage];
end

-- 打开技能学习界面
function UIHeroAttrib:showSkillLearnForm()
    -- 获取当前所需的最高阶职业
    local curDeptId = self:getCurDeptId();
    local careerList = CareerM.getCanLearnCareers(self.pet, curDeptId);

    if #careerList == 0 then
        -- error("配置错误，获取不到可学的称号。");
        trace("UIHeroAttrib", "配置错误，获取不到可学的称号。");
        return;
    end

    UIMgr.getCurrentScene():removeFormByName("UISkillLearn");
    local uiSkillLearn = UISkillLearn.create(careerList);
    UIMgr.getCurrentScene():addForm(uiSkillLearn);
end

-- 打开技能升级界面
function UIHeroAttrib:showSkillUpgradeForm(careerId)
    local curRank = CareerM.getCareerInfo(careerId, "rank");
    if curRank >= MAX_RANK then
        -- 该职业是最高阶，打开终极职业升级界面
        UIMgr.getCurrentScene():removeFormByName("UISkillUprade2");
        local uiSkillUpgrade2 = UISkillUpgrade2.create(careerId);
        UIMgr.getCurrentScene():addForm(uiSkillUpgrade2);
    else
        UIMgr.getCurrentScene():removeFormByName("UISkillUprade");
        local uiSkillUpgrade= UISkillUpgrade.create(careerId);
        UIMgr.getCurrentScene():addForm(uiSkillUpgrade);
    end
end

-- 设置分页按钮选中效果
function UIHeroAttrib:setPageSelected()
    -- 问号线路图按钮的动画弹出效果
    local careerBlue = findChildByName(self.node, "CT/career_blue");
    local careerRed  = findChildByName(self.node, "CT/career_line");
    if self.curPage == 1 then
        careerRed:setVisible(false);
        careerBlue:setVisible(true);
        careerBlue:setScale(0.6);
        careerBlue:setSwallowTouches(true);

        -- 开始重复动画
        -- 1.变大
        local upTime    = 0.25/4;
        local up2Time   = 0.25/4;
        local scaleUp   = cc.ScaleTo:create(upTime, 0.9);
        local scaleUp2  = cc.ScaleTo:create(up2Time, 1.2);

        local seq1 = cc.Sequence:create(scaleUp, scaleUp2);
        local action1 = cc.Spawn:create(seq1);

        -- 2.短暂停顿
        local delayTime = 0.2/4;
        local delay = cc.DelayTime:create(delayTime);

        -- 3.变小
        local downTime  = 0.25/4;
        local down2Time = 0.25/4;
        local scaleDown = cc.ScaleTo:create(downTime, 1.1);
        local scaleDown2 = cc.ScaleTo:create(down2Time, 1);
        local seq2 = cc.Sequence:create(scaleDown, scaleDown2);
        local action2 = cc.Spawn:create(seq2);

        local sequenceNodeAction = cc.Sequence:create(action1, delay, action2);
        careerBlue:runAction(sequenceNodeAction);
    else
        careerBlue:setVisible(false);
        careerBlue:setSwallowTouches(false);
        careerRed:setVisible(true);
        careerRed:setScale(0.5);

        -- 开始重复动画
        -- 1.变大
        local upTime    = 0.25/4;
        local up2Time   = 0.25/4;
        local scaleUp   = cc.ScaleTo:create(upTime, 0.9);
        local scaleUp2  = cc.ScaleTo:create(up2Time, 1.2);

        local seq1 = cc.Sequence:create(scaleUp, scaleUp2);
        local action1 = cc.Spawn:create(seq1);

        -- 2.短暂停顿
        local delayTime = 0.2/4;
        local delay = cc.DelayTime:create(delayTime);

        -- 3.变小
        local downTime  = 0.25/4;
        local down2Time = 0.25/4;
        local scaleDown = cc.ScaleTo:create(downTime, 1.1);
        local scaleDown2 = cc.ScaleTo:create(down2Time, 1);
        local seq2 = cc.Sequence:create(scaleDown, scaleDown2);
        local action2 = cc.Spawn:create(seq2);

        local sequenceNodeAction = cc.Sequence:create(action1, delay, action2);
        careerRed:runAction(sequenceNodeAction);
    end

    -- 当前页的系别
    self:updateDept();

    self:flyOut();
end

-- 显示技能
function UIHeroAttrib:redrawSkill()
    -- 获取已激活的系别
    local deptList  = CareerM.getDepartmentList(self.pet);
    local deptOrder = self.pet:query("department_order");
    local activeNum = #deptList;

    -- 选中和未选中的颜色设置
    local normalColor = TextStyleM.TEXT_COLOR_NORMAL;
    local blendColor = TextStyleM.TEXT_COLOR_BLEND;

    -- 显示技能分页按钮
    local pagePart = self.pagePart;
    local kerning = 0;
    if not isFullWidthLang() then
        kerning = -3;
    end
    for i = 1, SKILL_STYLE_NUM do
        local btn = findChildByName(pagePart, "btn_page" .. i);

        -- 仅显示已激活的系别
        if i <= activeNum then
            btn:setVisible(true);
            local style = deptList[i];
            local name = CareerM.getDepartmentInfo(style, "name");
            local nameLabel = findChildByName(btn, "name");
            local numLabel  = findChildByName(btn, "num");

            if i == 1 then
                TextStyleM.setTextStyle(nameLabel, TextStyleM.TEXT_SIZE_SMALL, TextStyleM.TEXT_COLOR_BROWN_YELLOW, true, kerning);
                btn:setTitleText("");
                nameLabel:setString(name);
            else
                -- 其他类型的系别，需要特殊显示
                TextStyleM.setTextStyle(nameLabel, TextStyleM.TEXT_SIZE_SMALL, TextStyleM.TEXT_COLOR_BROWN_YELLOW, true, kerning);
                TextStyleM.setTextStyle(numLabel, TextStyleM.TEXT_SIZE_TINY, TextStyleM.TEXT_COLOR_BROWN_YELLOW, kerning);
                btn:setTitleText("");
                nameLabel:setString(name);
                numLabel:setString("×2");
            end
        else
            btn:setVisible(false);
        end

        TextStyleM.setTextStyle(btn, TextStyleM.TEXT_SIZE_SMALL, TextStyleM.TEXT_COLOR_BROWN_YELLOW, true);

        local light = findChildByName(pagePart, "light" .. i);
        local nameLabel = findChildByName(btn, "name");

        -- 设置分页按钮选中效果
        if i == self.curPage then
            btn:setTouchEnabled(false);
            btn:setBright(false);
            btn:setColor(normalColor);
            light:setVisible(true);
        else
            btn:setTouchEnabled(true);
            btn:setBright(true);
            btn:setColor(blendColor);
            light:setVisible(false);
        end
    end

    -- 获取职业概要
    local careerData = self.pet:getCareerData(self:getCurDeptId());
    local openCareerRank = #careerData + 1;

    -- 显示技能框
    local skillPanel = findChildByName(self.node, "CT/skill_panel");
    for i = 1, MAX_SKILL_NUM do
        local child = findChildByName(skillPanel, "skill" .. i);
        if child then
            local rank = i;
            local careerId;
            if careerData[rank] then
                -- 已学习
                careerId = careerData[rank]["career"];
            elseif rank == openCareerRank then
                -- 可学习
                careerId = 0;
            else
                -- 未激活
                careerId = -1
            end

            -- 绑定职业编号
            child.careerId = careerId;

            -- 获取技能所处的状态
            local state = self:getCareerState(careerId);

            -- 根据技能状态绘制技能图标
            self:redrawSkillByState(child, state, rank, i);
        end
    end
end

-- 获取职业所处的状态
function UIHeroAttrib:getCareerState(careerId)
    if careerId > 0 then
        -- 已学习
        return SKILL_STATE_LEARNED;
    elseif careerId == 0 then
        -- 可学习
        return SKILL_STATE_CAN_LEARN;
    else
        -- 未知状态
        return SKILL_STATE_UNKOWN;
    end
end

-- 根据技能状态绘制技能图标
function UIHeroAttrib:redrawSkillByState(child, state, rank, i)
    -- 获取当前地牢开放的职业最高阶位
    local maxRank = CareerM.getDungeonMaxCareerRank(self:getCurDeptId());
    if rank > maxRank then
        -- 该阶位的职业尚未开放，直接隐藏
        child:setVisible(false);
        return;
    end

    child:setVisible(true);

    local canLearnMask = findChildByName(child, "mask_can_learn");
    local iconImg      = findChildByName(child, "skill_icon");
    local starNode     = findChildByName(child, "star_node");
    local frameImg     = findChildByName(child, "skill_frame");
    starNode:setVisible(false);

    -- 默认显示半透明的框
    frameImg:loadTexture("images/ui/frame/frame_metal2.png");

    iconImg:setScale(0.8);

    if state == SKILL_STATE_CAN_LEARN then
        -- 可学习
        -- 显示可学习的遮罩，隐藏不可学习的遮罩和技能图标
        iconImg:setVisible(false);
        canLearnMask:setVisible(true);

        -- 判断是否满足学习条件,播放界面动画
        self:learnTitleAnimation(child);

    elseif state == SKILL_STATE_LEARNED then
        -- 已学习
        -- 显示技能图标
        iconImg:setVisible(true);
        local careerId = child.careerId;
        local careerInfo = CareerM.getCareerInfo(child.careerId);
        local icon = careerInfo["icon"];
        iconImg:loadTexture(getCareerIconPath(icon));

        -- 隐藏可学习的遮罩
        canLearnMask:setVisible(false);

        -- 获取职业等级
        local level = CareerM.getCareerLevel(self.pet, careerId);

        -- 显示职业星级
        starNode:setVisible(true);
        local curStar  = level;
        local maxStar  = careerInfo["max_level"];

        local lightStarPath;
        local darkStarPath;
        local layer;

        -- 如果是最高阶职业，需要特殊显示
        if rank == MAX_RANK then
            -- 获取已满级的特性数量
            curStar = CareerM.getLevelUpperFeatureNum(self.pet, careerId);
            lightStarPath = "images/ui/symbol/moon_little_01.png";
            darkStarPath  = "images/ui/symbol/moon_little_02.png";

            local curArtifact = ArtifactM.getCarryArtifact();
            local cId = ArtifactM.query(curArtifact, "career_id");
            local maxLevel = FormulaM.invoke("CALC_CAREER_STRENGTHEN_LEVEL", curArtifact);
            local totalStar = MAX_FEATURE_NUM;
            if maxLevel >= 3 and cId == careerId then
                totalStar = MAX_FEATURE_NUM + 1;
                layer = createStarLayerEx(curStar, totalStar, lightStarPath, darkStarPath, "center", -0.4);
            else
                layer = createStarLayerEx(curStar, totalStar, lightStarPath, darkStarPath);
            end
        else
            lightStarPath = "images/ui/symbol/career_star_01.png";
            darkStarPath  = "images/ui/symbol/career_star_02.png";
            maxStar = math.min(maxStar, MAX_STAR_NUM);
            layer = createStarLayerEx(curStar, maxStar, lightStarPath, darkStarPath);
        end

        if layer then
            starNode:removeAllChildren();
            starNode:addChild(layer);
        end
    else
        -- 不可学习
        canLearnMask:setVisible(false);
        iconImg:setVisible(false);
    end

    self:skillFlyIn(child,i);
end

-- 获取对应称号节点
function UIHeroAttrib:getSkillChild(careerId)
    local skillPanel = findChildByName(self.node, "CT/skill_panel");
    for i = 1, MAX_SKILL_NUM do
        local child = findChildByName(skillPanel, "skill" .. i);
        if child and child.careerId == careerId then
            return child;
        end
    end

    return nil;
end

-- 技能框飞入效果
function UIHeroAttrib:skillFlyIn(child, i)
    local accelarateFactor = 2;
    local count = 0;

    local skillPanel = findChildByName(self.node, "CT/skill_panel");
    local panelWidth  = skillPanel:getContentSize().width;
    local panelHeight = skillPanel:getContentSize().height;

    local posY = panelHeight/2;

    local iconWidth = 90;
    local iconHeight = 90;

    -- 图标与图标直接的间距
    local gap = (panelWidth - iconWidth*MAX_SKILL_NUM) / (MAX_SKILL_NUM - 1);
    local posX = iconWidth / 2 + (i - 1)*(iconWidth + gap + 1);

    -- 物品飞入速度
    local speed = 2050 * accelarateFactor;
    local delay = cc.DelayTime:create(i * 0.1 / accelarateFactor);
    local moveTo = cc.MoveTo:create((900-posX) / speed, cc.p(posX, posY));
    if i == 1 then
        child:runAction(cc.Sequence:create(moveTo));
    else
        child:runAction(cc.Sequence:create(delay, moveTo));
    end
end

-- 更新技能框
function UIHeroAttrib:initPanel()
    local skillPanel = findChildByName(self.node, "CT/skill_panel");
    skillPanel:removeAllChildren();
    local panelWidth  = skillPanel:getContentSize().width;
    local panelHeight = skillPanel:getContentSize().height;

    local posY = panelHeight/2;

    local iconWidth  = 90;
    local iconHeight = 90;

    -- 图标与图标直接的间距
    local gap = (panelWidth - iconWidth*MAX_SKILL_NUM) / (MAX_SKILL_NUM - 1);

    for i = 1, MAX_SKILL_NUM do
        local child = cc.CSLoader:createNode("layout/hero/SkillIcon.csb");
        child:setScale(0.9);
        child:setName("skill" .. i);
        local posX = 900;
        child:setPosition(posX, posY);
        skillPanel:addChild(child);

        -- 注册技能框点击事件
        local skillFrame = findChildByName(child, "skill_frame");
        skillFrame:setTouchEnabled(true);
        local function onFrameClick(sender, eventType)
            if eventType == ccui.TouchEventType.ended then
                -- 针对指引做特殊处理，如果没有到学习称号的指引步骤，则不允许学习称号
                if ME.user.forbidToLearnSkill == true then
                    alert(getLocStr("forbid_to_learn_skill"));
                    return;
                end

                local careerId = child.careerId;

                -- 获取技能所处的状态
                local state = self:getCareerState(careerId);

                if state == SKILL_STATE_CAN_LEARN then
                    -- 打开技能学习界面
                    self:showSkillLearnForm();
                elseif state == SKILL_STATE_LEARNED then
                    -- 打开技能升级界面
                    self:showSkillUpgradeForm(careerId);
                else
                    -- 其他情况，不做处理
                end
            end
        end
        AddTouchEventListener(skillFrame, onFrameClick);
    end
end

-- 移除技能框，重绘新的技能框
function UIHeroAttrib:flyOut()
    local accelarateFactor = 2;

    local skillPanel = findChildByName(self.node, "CT/skill_panel");
    local panelWidth  = skillPanel:getContentSize().width;
    local speed = 2050 * accelarateFactor;
    local initPanel = cc.CallFunc:create(function()
        self:initPanel();
    end);

    local redrawSkillCallBack = cc.CallFunc:create(function()
        self:redrawSkill();
    end);

    local isInit = false;

    for i = 1, MAX_SKILL_NUM do
        local child = findChildByName(skillPanel, "skill" .. i);

        if child then
            local posX = child:getPositionX();
            local posY = child:getPositionY();

            local removeCallBack = cc.CallFunc:create(function()
                skillPanel:removeChild(child);
            end);

            if(0 < posX and posX < panelWidth) then
                local delay = cc.DelayTime:create(i * 0.1 / accelarateFactor);
                local moveTo = cc.MoveTo:create((60 + posX)/speed, cc.p(-60, posY));
                if i < MAX_SKILL_NUM then
                    -- 获取下个button的位置
                    local j = i + 1;
                    local nextChild = findChildByName(skillPanel, "skill" .. j);
                    local nextPosX = nextChild:getPositionX();

                    if(nextPosX > panelWidth) then
                        isInit = true;
                        child:runAction(cc.Sequence:create(moveTo, removeCallBack, initPanel, redrawSkillCallBack ));
                        break;
                    end

                    child:runAction(cc.Sequence:create(moveTo, removeCallBack));
                else
                    isInit = true;
                    child:runAction(cc.Sequence:create(moveTo, removeCallBack, initPanel, redrawSkillCallBack ));
                end
            end
        end
    end

    -- 技能格子必须被重绘
    if not isInit then
        self:initPanel();
        self:redrawSkill();
    end
end

-- 创建随扈的模型
function UIHeroAttrib:createLictorModel(petId, num, delayTime)
    local lictorPanel = findChildByName(self.node, "CT/left_part/lictor_panel_" .. num);
    local headImg   = findChildByName(lictorPanel, "lictor_head_" .. num);
    local modelNode = findChildByName(lictorPanel, "lictor_model_" .. num);

    if num == 1 then
        modelNode:setScaleX(-1);
    end

    -- 当前随扈的信息
    local pet = PetM.getMyPet(petId);
    local iconPath = getHeroLargeIconPath(pet:getLargeIcon());
    modelNode:removeAllChildren();

    local function createAnimation()
        local shapeId = ShapeChangeM.getShapeChangeId(petId);

        if shapeId ~= -1 then
            createShapeChangeMode(
                shapeId,
                headImg,
                ShapeChangeM.query(shapeId, "animation"),
                modelNode,
                0.44,
                getHeroLargeIconPath(ShapeChangeM.query(shapeId, "icon")));
        else
            createHeroModel(pet:getModelId(), headImg, modelNode, 0.44, iconPath);
        end
    end

    performWithDelay(self, createAnimation, delayTime);
end

-- 设置pet背景可见
function UIHeroAttrib:showHeroBg(petId, num, delayTime)
    local bgMain = findChildByName(self.node, "CT/bg_main");
    local heroBg = findChildByName(bgMain, "hero_bg");
    local heroBg2 = findChildByName(bgMain, "hero_bg2");
    heroBg:setVisible(true);
    heroBg2:setVisible(true);
end

-- 根据是否满足学习条件,播放界面动画
function UIHeroAttrib:learnTitleAnimation(node)
    if nil == node then
        return;
    end

    local canLearnMask = findChildByName(node, "mask_can_learn");
    local iconImg      = findChildByName(node, "skill_icon");
    -- 判断是否满足学习条件
    local function canLearn()
        local careers = CareerM.getCanLearnCareers(self.pet, self:getCurDeptId());
        for _, careerId in pairs(careers) do
            if careerId > 0 and true == CareerM.canJoin(self.pet.classId, careerId) then
                return true;
            end
        end
        return false;

    end

    canLearnMask:removeAllChildren();
    if canLearn() and ME.user.forbidToLearnSkill ~= true then
        -- 显示特效
        canLearnMask:setVisible(true);
        iconImg:setVisible(false);
        playLearnSkillHintEffect(canLearnMask, 54, 57);
    else
        iconImg:setVisible(true);
        canLearnMask:setVisible(false);
        iconImg:loadTexture("images/ui/common/add.png");
    end
end
